﻿using OpenCvSharp;
using OpenCvSharp.Features2D;
using OpenCvSharp.Flann;

class Program
{
    static void Main(string[] args)
    {
        // 读取图片
        //var image1 = Cv2.ImRead("d:/tmp/2/0.jpg", ImreadModes.Color);
        //var image2 = Cv2.ImRead("d:/tmp/2/11.jpg", ImreadModes.Color);
        var image1 = Cv2.ImRead("image1.jpg", ImreadModes.Color);
        var image2 = Cv2.ImRead("image3.jpg", ImreadModes.Color);

        // 强制调整图片尺寸
        var targetWidth = Math.Min(image1.Width, image2.Width); // 目标宽度
        var targetHeight = Math.Min(image1.Height, image2.Height); // 目标高度

        var targetSize = new Size(targetWidth, targetHeight);

        var resizedImage1 = new Mat();
        var resizedImage2 = new Mat();
        Cv2.Resize(image1, resizedImage1, targetSize);
        Cv2.Resize(image2, resizedImage2, targetSize);

        // 对齐图片
        var alignedImage1 = AlignImages(resizedImage1, resizedImage2);

        var resizedImage3 = new Mat();

        Cv2.Resize(alignedImage1, resizedImage3, targetSize);

        // 显示差异
        ShowDifferences(resizedImage3, resizedImage2);
    }

    /// <summary>
    /// 图片对齐
    /// </summary>
    /// <param name="image1"></param>
    /// <param name="image2"></param>
    /// <returns></returns>
    private static Mat AlignImages(Mat image1, Mat image2)
    {
        // 转换为灰度图像
        var gray1 = new Mat();
        var gray2 = new Mat();
        Cv2.CvtColor(image1, gray1, ColorConversionCodes.BGR2GRAY);
        Cv2.CvtColor(image2, gray2, ColorConversionCodes.BGR2GRAY);

        // 使用SIFT检测特征点和描述符
        var sift = SIFT.Create();
        Mat descriptors1 = new Mat(), descriptors2 = new Mat();
        sift.DetectAndCompute(gray1, null, out var keyPoints1, descriptors1);
        sift.DetectAndCompute(gray2, null, out var keyPoints2, descriptors2);

        // FLANN算法
        var indexParams = new AutotunedIndexParams(); // 数据结构采用k-d树进行索引
        var searchParams = new SearchParams();

        // 使用FLANN匹配器进行匹配
        var matcher = new FlannBasedMatcher(indexParams, searchParams);
        var matches = matcher.KnnMatch(descriptors1, descriptors2, k: 2);

        // 应用洛氏比率测试
        var goodMatches = new List<DMatch>();
        foreach (var matchPair in matches)
        {
            if (matchPair.Length > 1 && matchPair[0].Distance < 0.7 * matchPair[1].Distance)
            {
                goodMatches.Add(matchPair[0]);
            }
        }

        // 获取匹配点的坐标
        // 获取匹配点的坐标
        var srcPts = goodMatches.Select(m => new Point2d(keyPoints1[m.QueryIdx].Pt.X, keyPoints1[m.QueryIdx].Pt.Y)).ToArray();
        var dstPts = goodMatches.Select(m => new Point2d(keyPoints2[m.TrainIdx].Pt.X, keyPoints2[m.TrainIdx].Pt.Y)).ToArray();

        // 计算单应性矩阵
        var homography = Cv2.FindHomography(srcPts, dstPts, HomographyMethods.Ransac, 2.0);

        // 对齐图像1
        var size = image1.Size();
        var alignedImage1 = new Mat();
        Cv2.WarpPerspective(image1, alignedImage1, homography, size);

        return alignedImage1;
    }

    private static void ShowDifferences(Mat image1, Mat image2)
    {
        // 计算差异图像
        var diff = new Mat();
        Cv2.Absdiff(image1, image2, diff);
        var diffGray = new Mat();
        Cv2.CvtColor(diff, diffGray, ColorConversionCodes.BGR2GRAY);
        var diffMask = new Mat();
        Cv2.Threshold(diffGray, diffMask, 25, 255, ThresholdTypes.Binary);

        // 使用形态学操作连接小区域
        var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
        Cv2.MorphologyEx(diffMask, diffMask, MorphTypes.Open, kernel);

        // 在原图上标记差异区域
        var coloredDiff = new Mat();
        Cv2.CvtColor(diffMask, coloredDiff, ColorConversionCodes.GRAY2BGR);
        coloredDiff.SetTo(new Scalar(0, 0, 255), diffMask);
        var result = new Mat();
        Cv2.AddWeighted(image2, 1, coloredDiff, 1, 0, result);

        // 查找差异区域的轮廓
        var hierarchy = new Mat();
        Cv2.FindContours(diffMask, out var contours, hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);



        var rects = new List<Rect>();

        // 在差异区域绘制矩形框
        foreach (var contour in contours)
        {
            var boundingRect = Cv2.BoundingRect(contour);
            Cv2.Rectangle(result, boundingRect, new Scalar(0, 0, 255), 2);
            rects.Add(boundingRect);
        }

        //// 在原图上标记差异区域
        //var coloredDiff = new Mat();
        //Cv2.CvtColor(diffMask, coloredDiff, ColorConversionCodes.GRAY2BGR);
        //coloredDiff.SetTo(new Scalar(0, 0, 255), diffMask);
        //var result = new Mat();
        //Cv2.AddWeighted(image2, 1, coloredDiff, 1, 0, result);

        //// 查找差异区域的轮廓
        //var hierarchy = new Mat();
        //Cv2.FindContours(diffMask,out var contours, hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

        //// 在差异区域绘制矩形框
        //foreach (var contour in contours)
        //{
        //    var boundingRect = Cv2.BoundingRect(contour);
        //    Cv2.Rectangle(result, boundingRect, new Scalar(255, 0, 255), 2);
        //}

        // 显示结果
        Cv2.ImShow("Aligned Image 1", image1);
        Cv2.ImShow("Image 2", image2);
        //Cv2.ImShow("Difference", result);
        Cv2.ImShow("coloredDiff", coloredDiff);
        Cv2.ImShow("diffGray", diffGray);
        Cv2.ImShow("diff", diff);
        Cv2.ImShow("result", result);
        Cv2.WaitKey(0);
        Cv2.DestroyAllWindows();
    }
}